#include "comm/my_buf.h"
#include "comm/my_log.h"
#include "comm/my_crc.h"
#include "comm/my_mem.h"

void test_buf_chain()
{
    my_buf_chain_t *chain = my_buf_chain_alloc();

    my_buf_t *buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "aaa", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "bbb", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    my_buf_chain_free(chain);

    chain = my_buf_chain_alloc();

    buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "aaa", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "bbb", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    my_buf_chain_t *dst_chain = my_buf_chain_alloc();
    int64_t total_size = my_buf_chain_total_bytes(chain);
    my_buf_chain_copy_n(dst_chain, chain, total_size);
    my_buf_chain_consume(dst_chain, total_size);
    my_buf_chain_free(chain);
    my_buf_chain_free(dst_chain);
}

void test_crc32()
{
    my_buf_chain_t *chain = my_buf_chain_alloc();

    my_buf_t *buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "aaa", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "bbb", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "ccc", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "ddd", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    uint32_t crc = my_buf_chain_crc32(chain);

    my_buf_chain_t *dst_chain = my_buf_chain_alloc();
    my_buf_chain_copy(dst_chain, chain);

    uint32_t dst_crc;
    my_crc32_init(dst_crc);
    while ((buf = my_buf_chain_dequeue(dst_chain)) != NULL) {
        my_crc32_update(&dst_crc, (u_char *)buf->pos, (size_t)(buf->end - buf->pos));
        my_buf_free(buf);
    }
    my_crc32_final(dst_crc);

    if (dst_crc != crc) {
        MY_LOG_FATAL("except %ld = %ld", dst_crc, crc);
    }

    my_buf_chain_free(chain);
    my_buf_chain_free(dst_chain);
}

void test_consumer()
{
    my_buf_chain_t *chain = my_buf_chain_alloc();

    my_buf_t *buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "aaa", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "bbb", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "ccc", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "ddd", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    int64_t total_bytes = my_buf_chain_total_bytes(chain);
    if (total_bytes != 12) {
        MY_LOG_FATAL("except 12, but got %d", total_bytes);
    }

    my_buf_chain_consume(chain, 1);

    my_buf_chain_consume(chain, 11);

    total_bytes = my_buf_chain_total_bytes(chain);
    if (total_bytes != 0) {
        MY_LOG_FATAL("except 0, but got %d", total_bytes);
    }

    my_buf_chain_free(chain);
}

void test_consumer2()
{
    my_buf_chain_t *chain = my_buf_chain_alloc();

    my_buf_t *buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "aaa", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    my_buf_chain_consume(chain, 3);

    buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "bbb", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    my_buf_chain_consume(chain, 3);

    int64_t total_bytes = my_buf_chain_total_bytes(chain);
    if (total_bytes != 0) {
        MY_LOG_FATAL("except 0, but got %d", total_bytes);
    }

    my_buf_chain_free(chain);

}

void test_other()
{
    my_buf_chain_t *chain = my_buf_chain_alloc();

    my_buf_t *buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "aaa", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "bbb", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_alloc_ext(128);
    memcpy(buf->end, "ccc", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    buf = my_buf_aligned_alloc_ext(128);
    memcpy(buf->end, "ddd", 3);
    buf->end += 3;
    my_buf_chain_enqueue(chain, buf);
    my_buf_free(buf);

    uint32_t crc = my_buf_chain_crc32(chain);

    uint32_t dst_crc = 0;
    my_crc32_init(dst_crc);
    buf = my_buf_chain_first(chain);
    while (buf != NULL) {
        my_crc32_update(&dst_crc, (u_char *)buf->pos, (size_t)(buf->end - buf->pos));
        buf = my_buf_chain_next(chain, buf);
    }
    my_crc32_final(dst_crc);

    if (crc != dst_crc) {
        MY_LOG_FATAL("except %d, but got %d", crc, dst_crc);
    }

    buf = my_buf_chain_last(chain);
    if (buf->end - buf->pos != 3 || strncmp(buf->pos, "ddd", 3) != 0) {
        MY_LOG_FATAL("except ddd, but get other");
    }

    my_buf_chain_free(chain);
}


int main()
{
    test_buf_chain();
    test_crc32();
    test_consumer();
    test_consumer2();
    test_other();

    my_mem_assert_leak();

    MY_LOG_DEBUG("test pass");
    return 0;
}